using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Narwhunderful.Aphelia.Framework.Graphics;
using Narwhunderful.Aphelia.Framework.Input;
using Narwhunderful.Aphelia.Framework.Input.Keyboard;

namespace Narwhunderful.Aphelia.Framework.UI
{

    public abstract class UIComponent : SpriteComponent, IDesignableComponent, IKeyboardComponent, INavigableComponent, IUIComponent
    {

        Rectangle _previousScissor = Rectangle.Empty;

        public UIComponent(HUD hud)
            : base(hud.Game)
        {
            _hud = hud;
        }

        #region IUpdatableComponent

        public override bool NeedsUpdate
        {
            get
            {
                return base.NeedsUpdate;
            }
            set
            {
                base.NeedsUpdate = value;
                if (_parent != null && value) _parent.NeedsUpdate = true;
            }
        }

        protected override void InternalUpdate(GameTime gameTime)
        {
            base.InternalUpdate(gameTime);
            UpdateActualLocation(gameTime);
            UpdateScissor();
        }

        protected override void UpdateBounds(GameTime gameTime)
        {
            _borderSourceRectangle = new Rectangle(0, 0, base.SourceRectangle.Width, base.SourceRectangle.Height);
            _insideSourceRectangle = new Rectangle(base.SourceRectangle.X, base.SourceRectangle.Y, base.SourceRectangle.Width - (int)_borderSize.X, base.SourceRectangle.Height - (int)_borderSize.Y);
            base.UpdateBounds(gameTime);
            UpdateInsideBounds(gameTime);
        }

        protected virtual void UpdateInsideBounds(GameTime gameTime)
        {
            _insideBounds = new Rectangle(base.Bounds.X + (int)_borderSize.X, base.Bounds.Y + (int)_borderSize.Y, base.Bounds.Width - (int)_borderSize.X, base.Bounds.Height - (int)_borderSize.Y); ;
        }

        protected override void UpdateRenderLocation(GameTime gameTime)
        {
            base.UpdateRenderLocation(gameTime);
            if (_parent != null) base.RenderLocation = new Vector2(_parent.ActualLocation.X + base.RenderLocation.X, _parent.ActualLocation.Y + base.RenderLocation.Y);
        }

        protected virtual void UpdateActualLocation(GameTime gameTime)
        {
            _actualLocation = base.Location;
            if (_parent != null) _actualLocation = new Vector2(_parent.ActualLocation.X + base.Location.X, _parent.ActualLocation.Y + base.Location.Y);
        }

        protected override void UpdateTransform(GameTime gameTime)
        {
            base.UpdateTransform(gameTime);
            if (_parent != null) base.Transform *= Matrix.CreateTranslation(new Vector3(_parent.ActualLocation, 0.0f));
        }

        protected virtual void UpdateScissor()
        {
            _scissorRectangle = DrawingHelper.RectangleIntersect(base.Bounds, base.Game.GraphicsDevice.Viewport.Bounds);
            if (_parent != null) _scissorRectangle = DrawingHelper.RectangleIntersect(_scissorRectangle, _parent.ScissorRectangle);
        }

        #endregion

        #region IDrawableComponent

        public override void Draw(GameTime gameTime)
        {
            this.Draw(gameTime, false);
        }

        public virtual void Draw(GameTime gameTime, bool drawFocusedComponent)
        {
            if (CanDraw(gameTime, drawFocusedComponent))
            {
                BeginDraw();
                if (CanDrawBackground(gameTime)) InternalDrawBackground(gameTime);
                if (CanDrawMiddleground(gameTime)) InternalDrawMiddleground(gameTime);
                if (CanDrawForeground(gameTime)) InternalDrawForeground(gameTime);
                EndDraw();
            }
        }

        public override bool CanDraw(GameTime gameTime)
        {
            return this.CanDraw(gameTime, false);
        }

        public virtual bool CanDraw(GameTime gameTime, bool drawFocusedComponent)
        {
            if (!drawFocusedComponent && _isFocused) return false;
            return base.CanDraw(gameTime);
        }

        protected override void BeginDraw()
        {
            base.BeginDraw();
            if ((bool)base.SpriteBatch.Tag) base.SpriteBatch.End();
            base.SpriteBatch.Tag = true;
            _previousScissor = base.Game.GraphicsDevice.ScissorRectangle;
            base.Game.GraphicsDevice.ScissorRectangle = _scissorRectangle;
            BeginSpriteBatch();
        }

        protected virtual void BeginSpriteBatch()
        {
            base.SpriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, _parentScreen.CurrentTransitionEffect.RasterizerState, _parentScreen.CurrentTransitionEffect.BasicEffect);
        }

        protected virtual bool CanDrawBorder(GameTime gameTime)
        {
            return _borderIsVisible;
        }

        protected virtual void InternalDrawBorder(GameTime gameTime)
        {
            base.SpriteBatch.Draw(base.SpriteBatch.GetPixelTexture(), base.RenderLocation, _borderSourceRectangle, _borderColor, base.Rotation, base.Origin, base.Scale, base.SpriteEffects, 0);
        }

        protected override void InternalDrawTexture(GameTime gameTime)
        {
            base.SpriteBatch.Draw(base.TextureInformation.Texture, base.RenderLocation + _borderSize, _insideSourceRectangle, base.TintColor, base.Rotation, base.Origin, base.Scale, base.SpriteEffects, 0);
        }

        protected override void InternalDrawBackground(GameTime gameTime)
        {
            if (CanDrawBorder(gameTime)) InternalDrawBorder(gameTime);
        }

        protected virtual void EndSpriteBatch()
        {
        }

        protected override void EndDraw()
        {
            base.Game.GraphicsDevice.ScissorRectangle = _previousScissor;
            base.EndDraw();
        }

        #endregion

        #region IBasicSpriteComponent

        public override Vector2 Location
        {
            get
            {
                return base.Location;
            }
            set
            {
                base.Location = value;
                base.NeedsUpdate = true;
            }
        }

        #endregion

        #region IKeybooardComponent

        public event EventHandler<KeyEventArgs> KeyDown = null;
        public event EventHandler<KeyEventArgs> KeyPress = null;
        public event EventHandler<KeyEventArgs> KeyUp = null;

        protected internal virtual void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (!_isFocused) return;
            KeyDown.SafeInvoke(sender, e);
        }

        protected internal virtual void OnKeyPress(object sender, KeyEventArgs e)
        {
            if (!_isFocused) return;
            KeyPress.SafeInvoke(sender, e);
        }

        protected internal virtual void OnKeyUp(object sender, KeyEventArgs e)
        {
            if (!_isFocused) return;
            KeyUp.SafeInvoke(sender, e);
        }

        #endregion

        #region INavigableComponent

        bool _isNavigable;
        public bool IsNavigable
        {
            get
            {
                return _isNavigable;
            }
            set
            {
                _isNavigable = value;
            }
        }

        NavigationState _navigationState;
        public NavigationState NavigationState
        {
            get
            {
                return _navigationState;
            }
            protected set
            {
                _navigationState = value;
            }
        }

        int _horizontalNavagationIndex = 0;
        public virtual int HorizontalNavagationIndex
        {
            get
            {
                return _horizontalNavagationIndex;
            }
            set
            {
                _horizontalNavagationIndex = value;
            }
        }

        int _verticalNavagationIndex = 0;
        public virtual int VerticalNavagationIndex
        {
            get
            {
                return _verticalNavagationIndex;
            }
            set
            {
                _verticalNavagationIndex = value;
            }
        }

        public virtual bool CanNavigate(NavigationDirection direction)
        {
            if (direction == NavigationDirection.None) return false;
            if (_hud.FocusedScreen == null) return false;
            return (_isNavigable);
        }

        public virtual bool Navigate(NavigationDirection direction)
        {
            if (!CanNavigate(direction)) return false;
            _navigationState.PreviousComponent = null;
            _navigationState.NewComponent = null;
            DoNavigation(direction);
            if (_navigationState == null) return false;
            if (_navigationState.NewComponent == null) return false;
            if (_navigationState.NewComponent == _navigationState.PreviousComponent) return false;
            _navigationState.NewComponent.Focus();
            return true;
        }

        protected virtual void DoNavigation(NavigationDirection direction)
        {
            _navigationState.NewComponent = this;
            _navigationState.PreviousComponent = (_hud.FocusedScreen == null) ? null : _hud.FocusedScreen.FocusedComponent;
        }

        #endregion

        #region IUIComponent

        public event EventHandler<EventArgs> Adding = null;
        public event EventHandler<EventArgs> Added = null;
        public event EventHandler<EventArgs> Removing = null;
        public event EventHandler<EventArgs> Removed = null;
        public event EventHandler<EventArgs> GotFocus = null;
        public event EventHandler<EventArgs> LostFocus = null;

        Rectangle _borderSourceRectangle = Rectangle.Empty;
        Rectangle _insideSourceRectangle = Rectangle.Empty;

        HUD _hud = null;
        public virtual HUD HUD
        {
            get
            {
                return _hud;
            }
        }

        string _name;
        public virtual string Name
        {
            get
            {
                return _name;
            }
            set
            {
                if (_name != null)
                {
                    if (!_hud.ComponentNames.ContainsKey(_name)) _hud.ComponentNames.Remove(_name);
                }
                _hud.ComponentNames.Add(value, this);
                _name = value;
            }
        }

        Vector2 _actualLocation = Vector2.Zero;
        public virtual Vector2 ActualLocation
        {
            get
            {
                return _actualLocation;
            }
            protected set
            {
                _actualLocation = value;
            }
        }

        Rectangle _insideBounds = Rectangle.Empty;
        public Rectangle InsideBounds
        {
            get
            {
                return _insideBounds;
            }
            set
            {
                _insideBounds = value;
            }
        }

        Rectangle _scissorRectangle = Rectangle.Empty;
        public Rectangle ScissorRectangle
        {
            get
            {
                return _scissorRectangle;
            }
            protected set
            {
                _scissorRectangle = value;
            }
        }

        UIComponent _parent = null;
        public virtual UIComponent Parent
        {
            get
            {
                return _parent;
            }
            protected internal set
            {
                _parent = value;
            }
        }

        UIScreen _parentScreen = null;
        public virtual UIScreen ParentScreen
        {
            get
            {
                return _parentScreen;
            }
            protected internal set
            {
                _parentScreen = value;
            }
        }

        bool _canFocus = true;
        public bool CanFocus
        {
            get
            {
                return _canFocus;
            }
            set
            {
                _canFocus = value;
            }
        }

        bool _isFocused = false;
        public bool IsFocused
        {
            get
            {
                return _isFocused;
            }
        }

        Vector2 _borderSize = Vector2.Zero;
        public virtual int BorderWidth
        {
            get
            {
                return (int)_borderSize.X;
            }
            set
            {
                _borderSize.X = value;
            }
        }

        public virtual int BorderHeight
        {
            get
            {
                return (int)_borderSize.Y;
            }
            set
            {
                _borderSize.Y = value;
            }
        }

        Color _borderColor = Color.White;
        public Color BorderColor
        {
            get
            {
                return _borderColor;
            }
            set
            {
                _borderColor = value;
            }
        }

        bool _borderIsVisible;
        public bool BorderIsVisible
        {
            get
            {
                return _borderIsVisible;
            }
            set
            {
                _borderIsVisible = value;
            }
        }

        public virtual void Focus()
        {
            if (_isFocused) return;
            if (!_canFocus) return;
            ChangeFocusedComponent();
            _isFocused = true;
            this.OnAdding(this, new EventArgs());
        }

        public virtual void LoseFocus()
        {
            _isFocused = false;
            LoseFocusedComponent();
            OnLostFocus(this, new EventArgs());
        }

        internal virtual void ChangeFocusedComponent()
        {
            if (_parentScreen.FocusedComponent != null) _parentScreen.FocusedComponent.LoseFocus();
            _parentScreen.FocusedComponent = this;
        }

        internal virtual void LoseFocusedComponent()
        {
            _parentScreen.FocusedComponent = null;
        }

        protected internal virtual void OnAdding(object sender, EventArgs e)
        {
            BeginDesigning();
            Adding.SafeInvoke(sender, e);
        }

        protected internal virtual void OnAdded(object sender, EventArgs e)
        {
            EndDesigning();
            Added.SafeInvoke(sender, e);
        }

        protected internal virtual void OnRemoving(object sender, EventArgs e)
        {
            if (_hud.ComponentNames.ContainsKey(_name)) _hud.ComponentNames.Remove(_name);
            if (_isFocused) LoseFocus();
            Removing.SafeInvoke(sender, e);
        }

        protected internal virtual void OnRemoved(object sender, EventArgs e)
        {
            Removed.SafeInvoke(sender, e);
        }

        protected internal virtual void OnGotFocus(object sender, EventArgs e)
        {
            GotFocus.SafeInvoke(sender, e);
        }

        protected internal virtual void OnLostFocus(object sender, EventArgs e)
        {
            LostFocus.SafeInvoke(sender, e);
        }

        #endregion

    }

}
